/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  lock.h
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2015/12/18
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#pragma once

#include "../core.h"
#pragma warning(disable : 4251)
#include <mutex>

#ifdef WIN32
#include <windows.h>

#include <atomic>
namespace qb {
namespace base {

class S_CORE_EXPORT CSLock {
  CRITICAL_SECTION m_cs;

 private:
  CSLock(const CSLock& lock) { m_cs = lock.m_cs; }
  CSLock& operator=(const CSLock& lock) {
    m_cs = lock.m_cs;
    return *this;
  }

 public:
  CSLock() { InitializeCriticalSection(&m_cs); }
  explicit CSLock(int spinCount) {
    InitializeCriticalSectionAndSpinCount(&m_cs, spinCount);
  }
  ~CSLock() { DeleteCriticalSection(&m_cs); }
  void SetSpinCount(int count) { SetCriticalSectionSpinCount(&m_cs, count); }
  void Lock() { EnterCriticalSection(&m_cs); }
  void Unlock() { LeaveCriticalSection(&m_cs); }
};
class S_CORE_EXPORT Spinlock {  // 不可重入
 private:
  typedef enum { Locked, Unlocked } LockState;
  typedef Spinlock LockType;
  std::atomic<LockState> state_;

 public:
  Spinlock() : state_(Unlocked) {}
  void Lock() {
    while (state_.exchange(Locked, std::memory_order_acquire) == Locked) {
      /* busy-wait */
    }
  }
  void Unlock() { state_.store(Unlocked, std::memory_order_release); }
};

template <class LOCK>
class ScopedLock {
  LOCK& m_lock;

 private:
  ScopedLock(const ScopedLock& sl) {}
  ScopedLock& operator=(const ScopedLock& sl) { return *this; }

 public:
  ScopedLock(LOCK& lock) : m_lock(lock) { m_lock.Lock(); }
  ~ScopedLock() { m_lock.Unlock(); }
};

class S_CORE_EXPORT Mutex {
 public:
  Mutex() {}
  Mutex(const wchar_t* name) {}
  ~Mutex() {}

  void Lock() { m_mutex.lock(); }
  bool TryLock() { return m_mutex.try_lock(); }
  void Unlock() { m_mutex.unlock(); }

 private:
  std::recursive_mutex m_mutex;
};

template <class MUTEX>
class ScopedMutex {
  MUTEX& m_mutex;

 public:
  ScopedMutex(MUTEX& lock) : m_mutex(lock) { m_mutex.Lock(); }
  ~ScopedMutex() { m_mutex.Unlock(); }
};

class S_CORE_EXPORT Event {
  HANDLE m_handle;

 public:
  Event(bool manualReset, bool initSignaled, const wchar_t* name = 0);
  bool IsValid() { return m_handle != 0 && m_handle != INVALID_HANDLE_VALUE; }
  void Signal() { ::SetEvent(m_handle); }
  void Reset() { ::ResetEvent(m_handle); }
  DWORD Wait(DWORD timeout) { return WaitForSingleObject(m_handle, timeout); }
};
}  // namespace base
}  // namespace qb

#else
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>

namespace qb {
namespace base {

class CSLock {
  pthread_mutex_t m_cs;

 private:
  CSLock(const CSLock& lock) {
    m_cs = lock.m_cs;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&m_cs, &attr);
  }
  CSLock& operator=(const CSLock& lock) {
    m_cs = lock.m_cs;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&m_cs, &attr);
    return *this;
  }

 public:
  CSLock() {
    m_cs = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&m_cs, &attr);
  }
  explicit CSLock(int spinCount) {
    m_cs = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&m_cs, &attr);
  }
  ~CSLock() {}
  void SetSpinCount(int count) {}
  void Lock() { pthread_mutex_lock(&m_cs); }
  void Unlock() { pthread_mutex_unlock(&m_cs); }
};

#ifdef CCORE_MAC
class S_CORE_EXPORT Spinlock {
 private:
  void* ul;

 public:
  Spinlock();
  ~Spinlock();
  void Lock();
  void Unlock();
};
#else
class S_CORE_EXPORT Spinlock {
 private:
  pthread_spinlock_t sl;

 public:
  Spinlock() { pthread_spin_init(&sl, PTHREAD_PROCESS_PRIVATE); }
  ~Spinlock() { pthread_spin_destroy(&sl); }
  void Lock() { pthread_spin_lock(&sl); }
  void Unlock() { pthread_spin_unlock(&sl); }
};
#endif

template <class LOCK>
class ScopedLock {
  LOCK& m_lock;

 private:
  ScopedLock(const ScopedLock& sl) {}
  ScopedLock& operator=(const ScopedLock& sl) { return *this; }

 public:
  ScopedLock(LOCK& lock) : m_lock(lock) { m_lock.Lock(); }
  ~ScopedLock() { m_lock.Unlock(); }
};

class Mutex {
 public:
  Mutex() {}
  Mutex(const wchar_t* name) {}
  ~Mutex() {}

  void Lock() { m_mutex.lock(); }
  bool TryLock() { return m_mutex.try_lock(); }
  void Unlock() { m_mutex.unlock(); }

 private:
  std::recursive_mutex m_mutex;
};

template <class MUTEX>
class ScopedMutex {
  MUTEX& m_mutex;

 public:
  ScopedMutex(MUTEX& lock) : m_mutex(lock) { m_mutex.Lock(); }
  ~ScopedMutex() { m_mutex.Unlock(); }
};

class MutexGuard {
  pthread_mutex_t m_m;

 public:
  MutexGuard(pthread_mutex_t& m) : m_m(m) { pthread_mutex_lock(&m_m); }
  ~MutexGuard() { pthread_mutex_unlock(&m_m); }
};

class S_CORE_EXPORT Event {
  pthread_mutex_t m_mutex;
  pthread_cond_t m_cond;
  bool m_bValid = false;

 public:
  Event() {
    pthread_mutexattr_t attr;
    if (pthread_mutexattr_init(&attr) != 0) {
      m_bValid = false;
    } else {
      m_bValid = true;
    }
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&m_mutex, &attr);
    pthread_cond_init(&m_cond, NULL);
  }
  ~Event() {
    pthread_mutex_destroy(&m_mutex);
    pthread_cond_destroy(&m_cond);
  }

  bool IsValid() { return m_bValid; }
  void Signal() {
    MutexGuard g(m_mutex);
    pthread_cond_signal(&m_cond);
  }
  void Reset() {
    MutexGuard g(m_mutex);
    pthread_cond_destroy(&m_cond);
    pthread_cond_init(&m_cond, NULL);
  }
  int Wait(int timeout) {
    timespec Waittime;
    struct timeval now;
    gettimeofday(&now, NULL);
    int nsec = (timeout % 1000) * 1000000;
    Waittime.tv_nsec = nsec % 1000000000;
    Waittime.tv_sec = now.tv_sec + timeout / 1000;
    MutexGuard g(m_mutex);
    return pthread_cond_timedwait(&m_cond, &m_mutex, &Waittime);
  }
};
}  // namespace base
}  // namespace qb
#endif

#define SCOPED_LOCK(LOCK) qb::base::ScopedLock<qb::base::CSLock> _scopedl(LOCK);
#define SCOPED_MUTEX(MUTEX) \
  qb::base::ScopedMutex<qb::base::Mutex> _scopedm(MUTEX);
